package com.ahu.qifei.fund.service.impl;

import com.ahu.qifei.fund.common.enums.FenghuolunTaskStatusEnum;
import com.ahu.qifei.fund.common.enums.ResultEnum;
import com.ahu.qifei.fund.common.excetpion.ApiException;
import com.ahu.qifei.fund.common.util.Assert;
import com.ahu.qifei.fund.common.util.RSA;
import com.ahu.qifei.fund.service.IFenghuolunService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.LaxRedirectStrategy;

import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author ShiQifei
 * @date 2020/11/25 13:40
 */
@Service
@Slf4j
public class FenghuolunServiceImpl implements IFenghuolunService {
    private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    @Autowired
    RestTemplate restTemplate;
    // 项目id
    private String _AKPROJECT_ID_;
    // token
    private String _TB_TOKEN_;
    // 用户id
    private String _USER_ID_;
    // 默认任务名称
    private static final String[] defaultTaskNameArr = {"接口文档编写", "对接接口", "联调接口", "需求讨论，分析接口", "上线文件整理，部署"};
    // 自定义任务名称
    private List<String> myTaskNameArr = new ArrayList<>();
    // 当前迭代
    private JSONObject currentSprint;

    // 风火轮用户名密码
    private String userName;
    private String password;
    // header里面的Cookie
    CookieStore cookieStore;

    /**
     * 任务初始话，必须调用
     */
    @Override
    public void initial(String userName, String password) {
        Assert.notNull(myTaskNameArr, ResultEnum.INVALID_PARAM);
        myTaskNameArr.addAll(Arrays.asList(defaultTaskNameArr));

        Assert.notNull(userName, ResultEnum.INVALID_PARAM);
        Assert.notNull(password, ResultEnum.INVALID_PARAM);
        this.userName = userName;
        this.password = password;
        // 设置认证的cookie
        cookieStore = new BasicCookieStore();
        setCookie();
        setAKProjectID();
        Assert.notNull(_AKPROJECT_ID_, ResultEnum.INVALID_PARAM);
        // 设置用户信息
        setUserInfo();
        Assert.notNull(_TB_TOKEN_, ResultEnum.INVALID_PARAM);
        Assert.notNull(_USER_ID_, ResultEnum.INVALID_PARAM);

        // 获取迭代列表
        getSprintList();
        Assert.notNull(currentSprint, ResultEnum.INVALID_PARAM);
    }

    /***
     * 支持
     * @param userName
     * @param password
     * @param customTaskNameArr
     */
    public void initial(String userName, String password, String[] customTaskNameArr) {
        Assert.notNull(myTaskNameArr, ResultEnum.INVALID_PARAM);
        if(customTaskNameArr != null || customTaskNameArr.length > 0) {
            myTaskNameArr.addAll(Arrays.asList(customTaskNameArr));
        }
        initial(userName, password);
    }
    /**
     * 获取RSA加密后，再urlencode得到密码
     * @return
     */
    private String passwordRSA(String password) {
        try{
            String enrypt= RSA.encrypt(password, RSA_PUBLIC_KEY);
            return URLEncoder.encode(enrypt, "gb2312");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new ApiException(e.getMessage());
        }
    }
    /**
     * 获取cookie
     * 咱还不知道a_u
     * @return
     */
    private synchronized String getCookie() {
        StringBuffer cookieBuffer = new StringBuffer();
        cookieStore.getCookies().forEach(cookie -> {
            cookieBuffer.append(cookie.getName()).append("=").append(cookie.getValue()).append(";");
        });
        return cookieBuffer.toString();
    }

    /**
     * 获取可能的项目id。
     * 大多数情况下开发人员激活的project_id只有一个
     * 如果有两个，则直接抛出ApiException
     */
    private void setAKProjectID() {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Cookie", getCookie());
        HttpEntity<String> requestEntity = new HttpEntity<>(null, httpHeaders);
        // 没有传递原有的  staffId=internal_002313 参数
        String url = "http://devops.yundasys.com/api/getProjectList?_input_charset=utf-8&type=project&startTime=&pageSize=1&category=favours&akProjectTypeVPC=&akProjectTypeNameVPC=&toPage=1&treeNode=true";
        ResponseEntity<String> resultString = restTemplate.exchange(url, HttpMethod.GET,requestEntity,String.class);
        JSONObject result = JSON.parseObject(resultString.getBody());
        if("200".equals(result.getString("code"))) {
            JSONArray mainProjectArray = result.getJSONArray("result");
            mainProjectArray.forEach(mainProject ->{
                if(((JSONObject)mainProject).getBoolean("hasRight")) {
                    // 子项目数组
                    JSONArray childrenAKProjectArr = ((JSONObject)mainProject).getJSONArray("childrenAKProject");
                    childrenAKProjectArr.forEach(child -> {
                        if(((JSONObject) child).getBoolean("hasRight")) {
                            // 存在多个激活的子项目时或者父项目，则此值多次修改后为最后一个
                            _AKPROJECT_ID_ = ((JSONObject) child).getString("id");
                        }
                    });
                }
            });
        } else {
            throw new ApiException(resultString.getBody());
        }
    }

    /**
     *  设置用户信息
     */
    private void setUserInfo() {
        // 根据Cookie去获取_tb_token和 _user_id
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Cookie", getCookie());
        HttpEntity<String> requestEntity = new HttpEntity<>(null, httpHeaders);
        String url = "http://devops.yundasys.com/task?from=ak&akProjectId="+ _AKPROJECT_ID_;
        ResponseEntity<String> resultString = restTemplate.exchange(url, HttpMethod.GET,requestEntity,String.class);
        Document document = Jsoup.parse(resultString.getBody());
        _TB_TOKEN_ =  document.select("#_tb_token").val();
        _USER_ID_ =  document.select("#_user_id").val();
    }
    /**
     * 封装http中的cookie
     *  restTemplate会自动对进行了urlEncode的参数再次进行urlEncode
     *  可以通过使用URI的方式自动构建想要的url，这样避免restTemplate自动修改参数
     *  1. restTemplate会自动对302重定向，出发点是好的，但是不支持重定向过程中解析response的Set-Cookie,
     *      而下一次重定向是需要上一次的cookie数据的，导致重定向too many times
     *  2. Apache httpClient可以灵活的配置是否自动重定向且重定向过程中会默认解析保存Set-Cookie到CookieStore中
     * @return
     */
    private void setCookie() {
        CloseableHttpClient httpClient = HttpClientBuilder
                .create()
                // 允许循环重定向，默认是关闭的。
                // 风火轮认证路劲是循环的重定向
                //    路劲为： GET 302 /uc/login?from=http://devops.yundasys.com&ctx=/my
                //            GET 302 /my
                //            GET 302 /company/switchTo?callbackPath=/my
                //            GET 200 /my
                .setDefaultRequestConfig(RequestConfig.custom().setCircularRedirectsAllowed(true).build())
                // 为了拿到cookieStore的值，默认的是private，无法获取
                .setDefaultCookieStore(cookieStore)
                // 允许自动重定向
                .setRedirectStrategy(new LaxRedirectStrategy())
                .build();
        try {
            // 登录
            CloseableHttpResponse response = httpClient.execute(new HttpPost(URI.create("http://devops.yundasys.com/api/mix/custom/doLogin?"
                    + "username=" + userName + "&password=" + passwordRSA(password))));
            JSONObject result = JSON.parseObject(EntityUtils.toString(response.getEntity() , "UTF-8"));

            if(!result.getBoolean("success")) {
                httpClient.close();
                throw new ApiException(result.getString("message"));
            }
            // 获取一坨cookie，重定向数次
            httpClient.execute(new HttpPost(URI.create("http://devops.yundasys.com/uc/login?from=http%3A%2F%2Fdevops.yundasys.com&ctx=%2Fmy")));
            httpClient.close();
        } catch (IOException e) {
            log.error("内部异常，{}，{}",e.getMessage(), e);
            throw new ApiException(ResultEnum.SERVER_ERROR);
        }
    }

    /**
     * 获取迭代列表
     *
     * @return
     */
    @Override
    public JSONArray getSprintList() {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Cookie", getCookie());
        HttpEntity<String> requestEntity = new HttpEntity<>(null, httpHeaders);
        String url = "http://devops.yundasys.com/v3/issue/getSprintList?akProjectId="+ _AKPROJECT_ID_ + "&_=1598510971746&stamp=Task&sourceService=Task&order_column=created_at&order=desc";
        ResponseEntity<String> resultString = restTemplate.exchange(url, HttpMethod.GET,requestEntity,String.class);
        JSONObject result = JSON.parseObject(resultString.getBody());
        if("200".equals(result.getString("code"))) {
            JSONArray sprintList = result.getJSONArray("results");
            currentSprint = sprintList.getJSONObject(0);
            return sprintList;
        } else {
            throw new ApiException(resultString.getBody());
        }
    }

    /**
     * 查询待处理和处理中的任务
     *
     * @return
     */
    @Override
    public JSONArray queryTaskList() {
        String springId = currentSprint.getString("id");

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Cookie", getCookie());
        Map params = new HashMap();
        params.put("akProjectId", _AKPROJECT_ID_);
        params.put("stamp", "Task");
        params.put("sourceService", "Task");
        params.put("order_column", "created_at");
        params.put("order", "desc");
        // 待处理 处理中
        String statusId = FenghuolunTaskStatusEnum.NEW.getId() + "+" + FenghuolunTaskStatusEnum.DOING.getId();
        params.put("statusId", statusId);
        params.put("userId", _USER_ID_);
        params.put("sprintIds", springId);
        params.put("filterType", "Basic");
        params.put("isList", true);
        params.put("isNewK3", true);
        params.put("queryViewId", 260779);
        params.put("filterCondition", "statusId="+statusId+"&userId="+ _USER_ID_+"&sprintIds="+springId+"&filterType=Basic");
        HttpEntity<String> requestEntity = new HttpEntity(params, httpHeaders);
        String url = "http://devops.yundasys.com/v3/issue/list?_" + new Date().getTime();
        ResponseEntity<String> resultString = restTemplate.exchange(url, HttpMethod.GET,requestEntity,String.class);
        JSONObject result = JSON.parseObject(resultString.getBody());
        if("200".equals(result.getString("code"))) {
            return result.getJSONArray("results");
        } else {
            throw new ApiException(resultString.getBody());
        }
    }

    /**
     * 新增任务
     *
     * @return 任务id
     */
    @Override
    public String addNewTask() {
        LocalDate startDate = LocalDate.now();
        LocalDate endDate;
        // 正常情况下是两天后,礼拜四，礼拜五是四天后
        if(startDate.getDayOfWeek() == DayOfWeek.THURSDAY || startDate.getDayOfWeek() == DayOfWeek.FRIDAY) {
            endDate = startDate.plusDays(4);
        } else {
            endDate = startDate.plusDays(2);
        }

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Cookie", getCookie());
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, Object> queryMap= new LinkedMultiValueMap();
        // 新建的任务名称
        int randomIndex = Double.valueOf(Math.random() * myTaskNameArr.size()).intValue();
        queryMap.add("subject", currentSprint.getString("name") + "-" + myTaskNameArr.get(randomIndex));
        queryMap.add("assignedToId", _USER_ID_);
        queryMap.add("priorityId", 96);
        queryMap.add("sprintId", currentSprint.getString("id"));
        queryMap.add("akProjectId", _AKPROJECT_ID_);
        queryMap.add("relatedAKProjectIds", _AKPROJECT_ID_);
        queryMap.add("cf_79", startDate.format(dtf));
        queryMap.add("cf_80", endDate.format(dtf));
        queryMap.add("cf_136745", "新兴业务_点通达_微信商家小程序后端");
        queryMap.add("cf_136760", "需求任务");
        queryMap.add("cf_136763", "新兴业务");
        queryMap.add("cf_136764", "新系统");
        queryMap.add("_tb_token_", _TB_TOKEN_);
        queryMap.add("issueTypeId", 27);
        queryMap.add("stamp", "Task");

        HttpEntity<String> requestEntity = new HttpEntity(queryMap, httpHeaders);
        String url = "http://devops.yundasys.com/v3/issue/save";
        try{
            ResponseEntity<String> resultString = restTemplate.postForEntity(url,requestEntity,String.class);
            JSONObject result = JSON.parseObject(resultString.getBody());
            if("200".equals(result.getString("code"))) {
                return result.getJSONObject("results").getString("issueId");
            } else {
                throw new ApiException(resultString.getBody());
            }
        } catch(Exception e) {
            log.error(e.getMessage(), e);
            throw new ApiException(e.getMessage());
        }
    }

    /**
     * 更新任务
     *
     * @param issueId        任务id
     * @param taskStatusEnum 状态
     * @param workHours      工时 10/20
     * @param workProgress   进度 0,50,100
     * @return
     */
    @Override
    public Boolean updateTask(String issueId, FenghuolunTaskStatusEnum taskStatusEnum, int workHours, int workProgress) {

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Cookie", getCookie());
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, Object> queryMap= new LinkedMultiValueMap();
        queryMap.add("issueId", issueId);
        queryMap.add("_tb_token_", _TB_TOKEN_);
        // 任务状态
        queryMap.add("status_id", taskStatusEnum.getId());
        // 工时
        queryMap.add("cf_101587", workHours);
        // 进度
        queryMap.add("cf_100338", workProgress);
        queryMap.add("stamp", "Task");

        HttpEntity<String> requestEntity = new HttpEntity(queryMap, httpHeaders);
        String url = "http://devops.yundasys.com/v3/issue/update";
        ResponseEntity<String> resultString = restTemplate.postForEntity(url,requestEntity,String.class);
        JSONObject result = JSON.parseObject(resultString.getBody());
        if("200".equals(result.getString("code"))) {
            return true;
        } else {
            throw new ApiException(resultString.getBody());
        }
    }

    /**
     * 清除信息
     */
    @Override
    public void destory() {
        this.cookieStore = null;
        this.userName = null;
        this.password = null;
        this._USER_ID_ = null;
        this._AKPROJECT_ID_ = null;
        this._TB_TOKEN_ = null;
        this.currentSprint = null;
        this.myTaskNameArr = new ArrayList<>();
    }

    /**
     * 签到
     *
     * @param userName
     * @param password
     * @param customTaskNameArray
     */
    @Override
    public Boolean signIn(String userName, String password, String... customTaskNameArray) {
        // 初始化
        initial(userName, password, customTaskNameArray);
        // 查询待处理或者处理中的任务列表
        JSONArray unfinishedTaskList = queryTaskList();
        if(unfinishedTaskList == null || unfinishedTaskList.size() == 0) {
            // 新建待处理的任务
            String issueId = addNewTask();
            // 更新任务的工时，10个小时，进度 50%
            updateTask(issueId, FenghuolunTaskStatusEnum.DOING, 10 ,50);
        } else {
            // 获取第一个任务的id，一般情况下只会有一个没有完成的任务
            String issueId = unfinishedTaskList.getJSONObject(0).getJSONObject("columns").getString("id");
            updateTask(issueId, FenghuolunTaskStatusEnum.DONE, 20 ,100);
        }
        destory();
        return true;
    }
}
